package com.gitee.cirnochat.user.service.strategy.impl;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.cirnochat.chat.model.enums.MessageOperation;
import com.gitee.cirnochat.chat.model.vo.resp.ChatMessageResponse;
import com.gitee.cirnochat.chat.model.vo.resp.ChatOfflineMessageResponse;
import com.gitee.cirnochat.common.model.dto.NoticeMessage;
import com.gitee.cirnochat.common.model.dto.payload.FriendOnlinePayload;
import com.gitee.cirnochat.common.model.enums.SystemNoticeType;
import com.gitee.cirnochat.common.utils.RedissonLockUtil;
import com.gitee.cirnochat.user.model.entity.Friend;
import com.gitee.cirnochat.user.service.FriendService;
import com.gitee.cirnochat.user.service.strategy.UserStateStrategy;
import com.gitee.cirnochat.websocket.model.dto.WebSocketUserMessage;
import com.gitee.cirnochat.websocket.service.WebSocketService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Objects;
import java.util.Set;

import static com.gitee.cirnochat.common.constant.RedisKey.CHAT_OFFLINE_MESSAGE;
import static com.gitee.cirnochat.common.constant.RedisKey.CHAT_OFFLINE_MESSAGE_CONSUMED;
import static com.gitee.cirnochat.common.constant.WebSocketConstant.CHAT_SUBSCRIBE_CHANNEL;
import static com.gitee.cirnochat.common.constant.WebSocketConstant.CHAT_SYSTEM_CHANNEL;

@Component
@Slf4j
@RequiredArgsConstructor
public class UserOnlineStateStrategy implements UserStateStrategy {
    private final FriendService friendService;
    private final WebSocketService webSocketService;
    private final StringRedisTemplate redisTemplate;
    private final RedissonLockUtil redissonLockUtil;
    private final ObjectMapper objectMapper;

    @Override
    public void execute(Long userId) {
        log.info("用户 {} 上线", userId);
        sendOnlineNoticeToFriend(userId);
        publishOfflineMessageAsync(userId);
    }

    /**
     * 发送在线通知给好友
     * @param userId 上线的用户ID
     */
    private void sendOnlineNoticeToFriend(Long userId) {
        // 构建在线通知消息
        final FriendOnlinePayload friendOnlinePayload = new FriendOnlinePayload();
        friendOnlinePayload.setFriendId(userId);
        final NoticeMessage<FriendOnlinePayload> payload = new NoticeMessage<>();
        payload.setPayload(friendOnlinePayload);
        payload.setType(SystemNoticeType.FRIEND_ONLINE_NOTICE);
        log.info("发送好友上线通知，用户ID: {}", userId);
        // 获取用户的所有好友
        List<Friend> friends = friendService.getFriends(userId);
        // 对所有好友发送在线通知
        for (Friend friend : friends) {
            webSocketService.sendMessageToOnlineDevices(new WebSocketUserMessage(friend.getFriendId().toString(), CHAT_SYSTEM_CHANNEL, payload));
        }
    }


    /**
     * 异步发布离线消息
     * @param userId 用户ID
     */
    private void publishOfflineMessageAsync(Long userId) {
        //防止重复消费
        if (Boolean.FALSE.equals(redisTemplate.hasKey(CHAT_OFFLINE_MESSAGE_CONSUMED + userId))) {
            redissonLockUtil.redissonDistributedLocks(CHAT_OFFLINE_MESSAGE_CONSUMED + userId, () -> {
                final String redisListKey = CHAT_OFFLINE_MESSAGE + userId;
                if (Boolean.TRUE.equals(redisTemplate.hasKey(redisListKey))) {
                    Set<String> members = redisTemplate.opsForZSet().range(redisListKey, 0, -1);
                    if (members != null && !members.isEmpty()) {
                        List<ChatMessageResponse> offlineMessages = members.stream()
                                .map(member -> {
                                    try {
                                        return objectMapper.readValue(member, ChatMessageResponse.class);
                                    } catch (JsonProcessingException e) {
                                        log.error("解析离线消息失败: {}", member, e);
                                        return null;
                                    }
                                })
                                .filter(Objects::nonNull)
                                .toList();
                        ChatOfflineMessageResponse offlineMessageResponse = new ChatOfflineMessageResponse();
                        offlineMessageResponse.setContent(offlineMessages);
                        offlineMessageResponse.setOperation(MessageOperation.OFFLINE);
                        redisTemplate.opsForZSet().remove(redisListKey, members.toArray());
                        webSocketService.sendMessageToOnlineDevices(new WebSocketUserMessage(userId.toString(),CHAT_SUBSCRIBE_CHANNEL, offlineMessageResponse));
                    }
                }
            });
        }
    }
}
